/////http://www.disi.unige.it/person/MagilloP/GRAFICA04/pixmap.h

/*
File: rgb_read.c
Read a pixmap from file in RGB format.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rgb_read.h"

/*************************************************************************/
/* Contructor and destructor                                             */
/*************************************************************************/

RGBreader :: RGBreader(void)
{
  x_size = y_size = z_size = 0;
  min_pixel = 0;
  max_pixel = 255;
  starttab = lengthtab = NULL;
}

RGBreader :: ~RGBreader()
{
  if (starttab) free(starttab);
  if (lengthtab) free(lengthtab);
}

/*************************************************************************/
/* Implementation of abstract functions from superclass                  */
/*************************************************************************/

/* Filename is ok if extension is either .rgb or .RGB */
int RGBreader :: okFilename(char *filename)
{
  int l = strlen(filename);  
  if ((filename[l-4]=='.')&&(filename[l-3]=='r')&&
      (filename[l-2]=='g')&&(filename[l-1]=='b'))
     return 1;
  else
  if ((filename[l-4]=='.')&&(filename[l-3]=='R')&&
      (filename[l-2]=='G')&&(filename[l-1]=='B'))
     return 1;
  return 0;
}

/* Implementation of reading function */
int RGBreader :: readPixmap(FILE *input_fd, class Pixmap *map)
{  
  fd = input_fd; 
  if (readRGBheader())
  {
    map->make(x_size, y_size);
    if (readRGBimage(map)) return 1;
    else map->kill();
  }
  return 0;
}

/*************************************************************************/
/* Functions to read data from file                                      */
/*************************************************************************/

static unsigned short getshort(FILE *fd) /* FROM DOC */
{
  unsigned char buf[2]; 
  fread(buf,2,1,fd);
  return (buf[0]<<8)+(buf[1]<<0);
}

static unsigned long getlong(FILE *fd) /* FROM DOC */
{
  unsigned char buf[4];
  fread(buf,4,1,fd);
  return (unsigned long)(buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+(buf[3]<<0);
}

static unsigned char getbyte(FILE *fd) /* PAOLA */
{
  unsigned char buf[1];
  fread(buf,1,1,fd);
  return (buf[0]);
}

static void readlongtab(FILE *fd, int n, unsigned long *tab)
{
  int i;
  for (i=0; i<n; i++)  tab[i] = getlong(fd);
}

static void readbytetab(FILE *fd, int n, unsigned char *tab)
{
  int i;
  for (i=0; i<n; i++)  tab[i] = getbyte(fd);
}

/*************************************************************************/
/* Read file header, store information                                   */
/*************************************************************************/

int RGBreader :: readRGBheader()
{
    /* The header consists of 512 bytes as follows:
       2 bytes | short  | MAGIC     | IRIS image file magic number
       1 byte  | char   | STORAGE   | Storage format
       1 byte  | char   | BPC       | Number of bytes per pixel channel 
       2 bytes | ushort | DIMENSION | Number of dimensions
       2 bytes | ushort | XSIZE     | X size in pixels 
       2 bytes | ushort | YSIZE     | Y size in pixels 
       2 bytes | ushort | ZSIZE     | Number of channels
       4 bytes | long   | PIXMIN    | Minimum pixel value
       4 bytes | long   | PIXMAX    | Maximum pixel value
       4 bytes | char   | DUMMY     | Ignored
      80 bytes | char   | IMAGENAME | Image name
       4 bytes | long   | COLORMAP  | Colormap ID
     404 bytes | char   | DUMMY     | Ignored
   */

   int i;

   /* read magic number */
   magic = getshort(fd);
   if (magic!=474) 
   {  fprintf(stderr,"Bad magic number, not a SGI RGB image file\n");
      return 0;
   }
#ifdef DEBUG
   else fprintf(stderr,"SGI RGB image file\n");
#endif

   /* read storage format */
   storage = (unsigned short)getbyte(fd);
   if ((storage!=0)&&(storage!=1))
   {  fprintf(stderr,
              "Bad storage format (%d), not a SGI RGB image file\n",
              storage);
      return 0;
   }
#ifdef DEBUG
   switch (storage)
   {  case 0: fprintf(stderr,"stored VERBATIM\n"); break;
      case 1: fprintf(stderr,"stored RLE\n"); break;
   }
#endif

   /* read Number of bytes per pixel channel */
   bpc = (unsigned short)getbyte(fd);
   switch (bpc)
   {  case 1:
      case 2:
#ifdef DEBUG
         fprintf(stderr,"%d bytes per pixel (BPC)\n",bpc);
#endif
         break;
      default:
      {  fprintf(stderr,
                 "Bad number of bytes per pixels (%d), not a SGI RGB image file\n",
                 bpc);
         return 0;
      }
   }   

   /* read Number of dimensions */
   dimensions = getshort(fd);
   if ((dimensions<1)||(dimensions>3))
   {  fprintf(stderr,
     	      "Bad number of dimensions (%d), not a SGI RGB image file\n",
     	      dimensions);
      return 0;
   }
#ifdef DEBUG
   else fprintf(stderr,"%d dimensions\n",dimensions);
#endif

   /* read X and Y size in pixels */
   x_size = getshort(fd);
   y_size = getshort(fd);
#ifdef DEBUG
   fprintf(stderr,"x size = %d, y size = %d\n", x_size, y_size);
#endif

   /* read number of color channels */
   z_size = getshort(fd);
   if ((z_size!=1)&&(z_size!=3)&&(z_size!=4))
   {
         fprintf(stderr,
                 "Bad number of channels (%d), not a SGI RGB image file\n",
                 z_size);
         return 0;
   }
#ifdef DEBUG
   switch (z_size)
   {  case 1: fprintf(stderr,"z size = 1 (greyscale)\n"); break;
      case 3: fprintf(stderr,"z size = 3 (RGB)\n"); break;
      case 4: fprintf(stderr,"z size = 4 (RGB-Alpha)\n"); break;
   }   
#endif

   /* read min and max pixel value */
   min_pixel = getlong(fd);
   max_pixel = getlong(fd); 
#ifdef DEBUG
   fprintf(stderr,"Min and max pixel value = %d,%d\n", min_pixel, max_pixel);
#endif   

   /* read dummy characters */
   for (i=0; i<4; i++)   {  dummy = getbyte(fd);   }

   /* read image name */
   for (i=0; i<80; i++)  image_name[i] = getbyte(fd);
   image_name[80] = '\0';
#ifdef DEBUG
   fprintf(stderr,"Image name = %s\n", image_name);
#endif   

   /* read color map identifier */
   color_map = getlong(fd);
   if ((color_map<0)||(color_map>3))
   {  fprintf(stderr,"Bad color map (%d), not a SGI RGB image file\n",
              color_map);
      return 0;
   }
#ifdef DEBUG  
   switch (color_map)
   {  case 0: fprintf(stderr,"NORMAL color map\n"); break;
      case 1: fprintf(stderr,"DITHERED color map\n"); break;
      case 2: fprintf(stderr,"SCREEN color map\n"); break;
      case 3: fprintf(stderr,"COLORMAP color map\n"); break;
   }   
#endif
   
   /* read dummy characters */
   for (i=0; i<404; i++)   {  dummy = getbyte(fd);   }

   return 1;
} 

/*************************************************************************/
/* Read run-length tables (if run-length encoded) and pixel data         */
/*************************************************************************/

void RGBreader :: readtables(void) /* FROM DOC */
{
  tablen = y_size * z_size * sizeof(unsigned long);
  starttab = (unsigned long *) malloc(tablen);
  lengthtab = (unsigned long *) malloc(tablen);
  fseek(fd,512,SEEK_SET);
  readlongtab(fd, y_size * z_size, starttab);
  readlongtab(fd, y_size * z_size, lengthtab);
}

/*
Find the file offset and RLE data length for a scanline, FROM DOC 
r (row) is an integer in the range 0 to y_size-1
c (channel) is an integer in the range 0 to z_size-1
*/
int RGBreader :: rleoffset(int r, int c)
{  return starttab[r+c*y_size];  }
int RGBreader :: rlelength(int r, int c)
{  return lengthtab[r+c*y_size];  }

/*
If BPC is 1, then there is one byte per pixel (in each color channel).
The RLE data should be read into an array of chars.
To expand data, the low order seven bits of the first byte: bits[6..0]
are used to form a count.
If the high order bit of the first byte is 1: bit[7], 
then the count is used to specify how many
bytes to copy from the RLE data buffer to the destination.
Otherwise, if the high order bit of the first byte is 0: bit[7],
then the count is used to specify how many times to repeat the 
value of the following byte, in the destination.
This process continues until a count of 0 is found.
This should decompress exactly XSIZE pixels.
Decompress a scanline, FROM DOC 
*/
static void expandrow(unsigned char *optr, unsigned char *iptr, int z)
{
  /*
  iptr l'array di caratteri in input (letti dal file),
  optr l'array di caratteri in input (l'immagine)
  */
  unsigned char pixel, count;

  optr += z;
  while(1)
  {
     pixel = *iptr++;
     /* se count formato dai 7 bit a destra e' 0 esci */
     if ( !(count = (pixel & 0x7f)) ) return;
     /* prendi l'ottavo bit */
     /* se e' 1 copia count volte da input in output */
     if (pixel & 0x80)
     {  while(count--)
        {
           *optr = *iptr++;
           optr+=4;
        }
     }
     /* se e' 0 copia count volte questo valore in output */
     else
     {  pixel = *iptr++;
        while(count--) 
        {
           *optr = pixel;
           optr+=4;
        }
     }
   }
}


int RGBreader :: readscanline(Pixmap * map, int r, int c,
                            int which_color /* 0,1,2 for R,G,B */)
{
  unsigned char pixel, count;
  int i = 0, si = 0;
  unsigned char * iptr;
  int ind;
  
  /* lunghezza della scanline codificata */
  int l = rlelength(r,c); /*il numero di caratteri, non di bits ???? devo dividere per 8??? */
  /* inizio della scanline */
  int inizio = rleoffset(r,c);
  /* alloco e leggo la scanline */
  iptr = (unsigned char *)malloc(l*sizeof(unsigned char));
  fseek(fd,inizio,SEEK_SET);
  readbytetab(fd, l, iptr);
  /* la decodifico */

  while(1)
  {
     pixel = iptr[si]; si++;
     /* se count formato dai 7 bit a destra e' 0 esci */
     if ( !(count = (pixel & 0x7f)) )
     {  if (i<x_size)
        fprintf(stderr,"Unexpected end of scanline (%d < %d)\n",i,x_size);
        return 0;
     }
     /* prendi l'ottavo bit */
     /* se e' 1 copia count volte da input in output */
     if (pixel & 0x80)
     {  while(count--)
        {
          ind = map->pos(i,r);
          switch(which_color)
          {  case 0: map->pixels[ind].r = (unsigned short int)iptr[si];
                     break;
             case 1: map->pixels[ind].g = (unsigned short int)iptr[si];
                     break;
             case 2: map->pixels[ind].b = (unsigned short int)iptr[si];
                     break;
          }
          si++;
          i++;
       }
     }
     /* se e' 0 copia count volte questo valore in output */
     else
     {  pixel = iptr[si]; si++;
        while(count--) 
        {
          ind = map->pos(i,r);
          switch(which_color)
          {  case 0: map->pixels[ind].r = (unsigned short int)pixel;
                     break;
             case 1: map->pixels[ind].g = (unsigned short int)pixel;
                     break;
             case 2: map->pixels[ind].b = (unsigned short int)pixel;
                     break;
           }
           i++;
        }
    }
  }
  return 1;
}
 
/*
Read pixels and put their RGB components into the pixmap,
to be called after readRGBheader, PAOLA
*/
int RGBreader :: readRGBimage(class Pixmap * map)
{
  unsigned short int r_val, g_val, b_val; /* red, green, blue values */

  if (z_size>3)
  {  fprintf(stderr,"Sorry, only greyscale and RGB images are supported\n");
     return 0;
  }

  if (bpc!=1)
  {  fprintf(stderr,"Sorry, %d BPC not supported\n",bpc);
     return 0;
  }

  if (storage==0) /* VERBATIM */
  {
    int i, j, val;
    /* read red */
    for (j=0; j<y_size; j++)
    for (i=0; i<x_size; i++)
    {  
      unsigned char cc = getbyte(fd);
      r_val = (unsigned short int)cc;
      map->pixels[map->pos(i,j)].r = r_val;
    }
    if(z_size>1)
    {
      /* read green */
      for (j=0; j<y_size; j++)
      for (i=0; i<x_size; i++)
      {  if (z_size==3) g_val = (unsigned short int)getbyte(fd);
         else g_val = r_val;
         map->pixels[map->pos(i,j)].g = g_val;
      }
      /* read blue */
      for (j=0; j<y_size; j++)
      for (i=0; i<x_size; i++)
      {  if (z_size==3) b_val = (unsigned short int)getbyte(fd);
         else b_val = r_val;
         map->pixels[map->pos(i,j)].b = b_val;
      }
    }
#ifdef DEBUG
    fprintf(stderr,"Pixels have been read\n");
#endif
    return 1;
  }
  if (storage==1) /* RLE = Run Length Encoding */
  {
    int i;
    readtables();
#ifdef DEBUG
    fprintf(stderr,"Tables have been read\n");
#endif
    /* 
    Ci sono x_size * z_size scanlines, ciascuna deve
    dare luogo a x_size pixel.
    Ciascuna ha nella tabella uno start e un length.
    */
    for (i=0; i<y_size; i++)
         readscanline(map, i,0, 0); /* rosso oppure toni di grigio se l'immagine e' in greyscale */ 
    if (z_size>1)
    {
      for (i=0; i<y_size; i++)
         readscanline(map, i,1, 1); /* verde */
      for (i=0; i<y_size; i++)
         readscanline(map, i,2, 2); /* blu */
    }
#ifdef DEBUG
    fprintf(stderr,"Pixels have been read\n");
#endif
    return 1;
  }
}

/*************************************************************************/
